#pragma once
#include <iostream>
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <unistd.h>
using namespace std;

const static int default_num = 5;

template <class U>
using func_t = function<void(U)>;

template <class T, class U>
class threadpool
{
public:
    threadpool(int num = default_num) // 构造的时候就直接开始创建线程,等待任务到来
        : _num(num), _vt(_num)
    {
        for (int i = 0; i < _num; i++)
        {
            string name = "thread_" + to_string(i+1);
            //_vt[i] = thread(std::bind(&threadpool<T>::ThreadRun, this, placeholders::_1), name); // 线程不支持拷贝,移动赋值
            _vt[i] = thread(&threadpool<T, U>::ThreadRun, this, name); // 线程不支持拷贝,移动赋值
        }
    }

    void ThreadRun(const string &name)
    {
        while (true)
        {
            T t;
            {
                unique_lock<mutex> mtx(_mtx);
                while (_request_q.empty()) // 所以在唤醒的线程中还要继续判断是否为空
                {
                    // 线程在等待的时候会释放锁，所有线程起初都会在wait处进行等待
                    _cond.wait(mtx);
                }
                t = _request_q.front();
                _request_q.pop();

                // 出了作用域会自动释放锁资源
            }
            cout << name << ": ";
            U u = t(); // 任务处理，此时锁已经释放，其他线程依旧争夺锁资源
            _respons_q.push(u);
        }
    }

    void Push(const T &t)
    {
        _request_q.push(t);
        // 唤醒线程去处理任务
        _cond.notify_one();
    }

    bool IsData()
    {
        return !_respons_q.empty();
    }

    U Front()
    {
        if (!_respons_q.empty())
        {
            U u = _respons_q.front();
            _respons_q.pop();
            return u;
        }
        return nullptr;
    }

    ~threadpool()
    {
        for (int i = 0; i < _num; i++)
        {
            _vt[i].join();
        }
    }

private:
    int _num;            // 线程数量
    queue<T> _request_q; // 任务队列
    queue<U> _respons_q; // 响应队列
    vector<thread> _vt;  // 线程管理

    mutex _mtx;               // 锁
    condition_variable _cond; // 条件变量
};
